package cn.gzmli.dtt.common.utils.websocket;

import cn.gzmli.dtt.common.entity.User;
import cn.gzmli.dtt.common.service.log.LogExecutor;
import cn.gzmli.dtt.common.service.log.LoginLogService;
import cn.gzmli.dtt.common.service.log.UserOnlineLogService;
import cn.gzmli.dtt.common.config.cache.Config;
import cn.gzmli.dtt.common.utils.GetHttpSessionConfigurator;
import cn.gzmli.dtt.common.utils.JsonUtils;
import cn.gzmli.dtt.common.utils.SpringContextUtil;
import org.apache.log4j.Logger;
import cn.gzmli.dtt.common.service.MessageService;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpSession;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;

//该注解用来指定一个URI，客户端可以通过这个URI来连接到WebSocket。类似Servlet的注解mapping。无需在web.xml中配置。
@ServerEndpoint(value = "/msgwebsocket/{userId}",configurator=GetHttpSessionConfigurator.class)
@Component
public class MsgWebSocket {
    private static final Logger logger = Logger.getLogger(MsgWebSocket.class);
    private LoginLogService loginLogService;
    private UserOnlineLogService userOnlineLogService;

    //静态变量，用来记录当前在线连接数。应该把它设计成线程安全的。
    private static int onlineCount = 0;

    //concurrent包的线程安全Set，用来存放每个客户端对应的MyWebSocket对象。若要实现服务端与单一客户端通信的话，可以使用Map来存放，其中Key可以为用户标识
    public static CopyOnWriteArraySet<MsgWebSocket> webSocketSet = new CopyOnWriteArraySet<MsgWebSocket>();

    public static Map<Integer,CopyOnWriteArrayList<Session>> webSocketMap = new ConcurrentHashMap();

    //与某个客户端的连接会话，需要通过它来给客户端发送数据
    private Session session;
    private HttpSession httpSession;
    private static String sessionId;

    /**
     * 连接建立成功调用的方法
     * @param session  可选的参数。session为与某个客户端的连接会话，需要通过它来给客户端发送数据
     */
    @OnOpen
    public void onOpen(@PathParam("userId") Integer userId, Session session, EndpointConfig config){

        System.out.println("打开连接>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");

        this.session = session;
        this.httpSession = (HttpSession) config.getUserProperties()
                .get(HttpSession.class.getName());
        webSocketSet.add(this);     //加入set中
        addOnlineCount();           //在线数加1
        User user=(User)httpSession.getAttribute("User");
        if(user!=null){
            userId = user.getId();
        }

        // 存储用户的wsSession
        CopyOnWriteArrayList<Session> wssList;
        if(webSocketMap.get(userId)!=null&&webSocketMap.get(userId).size()>0){
            wssList = webSocketMap.get(userId);
        }else {
            wssList = new CopyOnWriteArrayList<>();
        }
        wssList.add(session);
        webSocketMap.put(userId,wssList);

//        List<HttpSession> userSessions=Config.loginedUserSessions.get(userId);
//        for(HttpSession httpSession:userSessions){
//            if(httpSession.getId().equals(this.httpSession.getId())){
//                httpSession.setAttribute("msgSocket",session);
//            }
//        }
        //System.out.println("新连接"+user.getName()+"加入！当前在线人数为" + getOnlineCount());


        sessionId = httpSession.getId();

    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(@PathParam("userId") Integer userId,Session session){
        webSocketSet.remove(this);  //从set中删除
        subOnlineCount();           //在线数减1
        System.out.println("有一连接关闭！当前在线人数为" + getOnlineCount());

        //清理失效的wsSession
        CopyOnWriteArrayList<Session> webSocketList = webSocketMap.get(userId);
        if(webSocketList!=null&&webSocketList.size()>0){
            webSocketList.remove(session);
            webSocketMap.put(userId,webSocketList);
        }

        /**
         * 添加或更新注销日志
         * modify by xsx 2018-03-23
         */
        addOrUpdateLog();
    }

    /**
     * 收到客户端消息后调用的方法
     * @param message 客户端发送过来的消息
     * @param session 可选的参数
     */
    @OnMessage
    public void onMessage(@PathParam("userId") Integer userId,String message, Session session)throws Exception {
        System.out.println("来自客户端的消息:" + message);
        this.session = session;
        ConcurrentHashMap<Integer,HttpSession> userSessions=Config.loginedUserSession;
        Set<Integer> userIDS= userSessions.keySet();
        //通过websocket会话获取http会话
        for(int userID:userIDS){
            HttpSession userSession=null;
            try {
                userSession = userSessions.get(userID);
                if (userSession.getAttribute("msgSocket") != null
                        && userSession.getAttribute("msgSocket") == session) {
                    this.httpSession = userSession;
                }
            }catch(Exception e){
                //移除过期会话
                if(userSessions!=null){
                    userSessions.remove(userID);
                }
                logger.error(e);
            }
        }

        //如果是获取未读消息数量
        if(message.equalsIgnoreCase("GetUnReadMsgCount")){
            getUnReadMsgCount(userId,this.httpSession,session);
        }
    }

    /**
     * 获取用户未读信息，并发送给客户端
     * @param httpSession
     * @param webSocket
     * @throws Exception
     */
    public static void getUnReadMsgCount(Integer userId,HttpSession httpSession, Session webSocket)throws Exception {
        MessageService messageService=(MessageService)SpringContextUtil.getBean("messageService");
        User user=(User)httpSession.getAttribute("User");
        if(user!=null){
            userId = user.getId();
        }
        int unReadMsgCount= messageService.queryUnReadMsgCount(userId);
        Map<String,Object> result=new HashMap<String,Object>();
        result.put("infoType","UnReadMsgCount");
        result.put("content",unReadMsgCount);
        webSocket.getBasicRemote().sendText(JsonUtils.beanToJson(result));
    }

    /**
     * 发生错误时调用
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error){
        System.out.println("发生错误");
        error.printStackTrace();
    }

    /**
     * @param message
     * @throws IOException
     */
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
        //this.session.getAsyncRemote().sendText(message);
    }

    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        MsgWebSocket.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        MsgWebSocket.onlineCount--;
    }
    
    


    /**
     * 添加或更新注销日志
     */
	private void addOrUpdateLog(){
        try {
            LogExecutor.execute(
                new Runnable(){
                    @Override
                    public void run() {
                        try{
                            User user = (User) httpSession.getAttribute("User");
                            if (user!=null){
                                if (loginLogService==null){
                                    loginLogService = (LoginLogService) SpringContextUtil.getBean("loginLogService");
                                }
                                loginLogService.saveLogout(sessionId, user);
                                if (userOnlineLogService==null){
                                    userOnlineLogService = (UserOnlineLogService) SpringContextUtil.getBean("userOnlineLogService");
                                }
                                userOnlineLogService.updateOnlineTime(sessionId, user);
                            }
                        }catch (Exception e){
                            // TODO
                        }
                    }
                }
            );
        } catch (Exception e) {
            logger.error("添加或更新注销日志异常", e);
        }
    }
}
